using System.Collections.Generic;

using Microsoft.VisualStudio.Package;
using Microsoft.VisualStudio.TextManager.Interop;

using Microsoft.WowAddonStudio.Lua.LanguageService.Model;
using Microsoft.WowAddonStudio.Lua.Parser;

using ColorableItem = Microsoft.WowAddonStudio.Lua.LanguageService.Model.ColorableItem;

namespace Microsoft.WowAddonStudio.Lua.LanguageService
{
    public static class Configuration
    {
        public const string Extension = ".lua";
        public const string Name = "Lua";

        private static readonly Dictionary<int, TokenDefinition> definitions = new Dictionary<int, TokenDefinition>();
        private static readonly List<IVsColorableItem> colorableItems = new List<IVsColorableItem>();

        private static readonly TokenDefinition defaultDefinition = new TokenDefinition(TokenType.Text, TokenColor.Text, TokenTriggers.None);
        private static readonly CommentInfo commentInfo;

        /// <summary>
        /// Initializes the <see cref="Configuration"/> class.
        /// </summary>
        static Configuration()
        {
            commentInfo = new CommentInfo
                          {
                              BlockStart = "--[[",
                              BlockEnd = "]]--",
                              LineStart = "-- ",
                              UseLineComments = true
                          };

            /*
            Configuration.CreateTokenColor("Keyword", COLORINDEX.CI_BLUE, COLORINDEX.CI_USERTEXT_BK);
            Configuration.CreateTokenColor("Comment", COLORINDEX.CI_DARKGREEN, COLORINDEX.CI_USERTEXT_BK);
            Configuration.CreateTokenColor("Identifier", COLORINDEX.CI_SYSPLAINTEXT_FG, COLORINDEX.CI_USERTEXT_BK);
            Configuration.CreateTokenColor("String", COLORINDEX.CI_RED, COLORINDEX.CI_USERTEXT_BK);
            Configuration.CreateTokenColor("Number", COLORINDEX.CI_SYSPLAINTEXT_FG, COLORINDEX.CI_USERTEXT_BK);
            Configuration.CreateTokenColor("Text", COLORINDEX.CI_SYSPLAINTEXT_FG, COLORINDEX.CI_USERTEXT_BK);
            */

            // TokenColor error = Configuration.CreateTokenColor("Error", COLORINDEX.CI_SYSPLAINTEXT_FG, COLORINDEX.CI_USERTEXT_BK, false, false);

            // Keywords
            AddTokenDefinition((int)Tokens.KWAND, TokenType.Keyword, TokenColor.Keyword, TokenTriggers.None);
            AddTokenDefinition((int)Tokens.KWBREAK, TokenType.Keyword, TokenColor.Keyword, TokenTriggers.None);
            AddTokenDefinition((int)Tokens.KWDO, TokenType.Keyword, TokenColor.Keyword, TokenTriggers.None);
            AddTokenDefinition((int)Tokens.KWEND, TokenType.Keyword, TokenColor.Keyword, TokenTriggers.None);
            AddTokenDefinition((int)Tokens.KWELSE, TokenType.Keyword, TokenColor.Keyword, TokenTriggers.None);
            AddTokenDefinition((int)Tokens.KWELSEIF, TokenType.Keyword, TokenColor.Keyword, TokenTriggers.None);
            AddTokenDefinition((int)Tokens.KWFOR, TokenType.Keyword, TokenColor.Keyword, TokenTriggers.None);
            AddTokenDefinition((int)Tokens.KWFALSE, TokenType.Keyword, TokenColor.Keyword, TokenTriggers.None);
            AddTokenDefinition((int)Tokens.KWFUNCTION, TokenType.Keyword, TokenColor.Keyword, TokenTriggers.None);
            AddTokenDefinition((int)Tokens.KWIF, TokenType.Keyword, TokenColor.Keyword, TokenTriggers.None);
            AddTokenDefinition((int)Tokens.KWIN, TokenType.Keyword, TokenColor.Keyword, TokenTriggers.None);
            AddTokenDefinition((int)Tokens.KWLOCAL, TokenType.Keyword, TokenColor.Keyword, TokenTriggers.None);
            AddTokenDefinition((int)Tokens.KWNIL, TokenType.Keyword, TokenColor.Keyword, TokenTriggers.None);
            AddTokenDefinition((int)Tokens.KWNOT, TokenType.Keyword, TokenColor.Keyword, TokenTriggers.None);
            AddTokenDefinition((int)Tokens.KWOR, TokenType.Keyword, TokenColor.Keyword, TokenTriggers.None);
            AddTokenDefinition((int)Tokens.KWREPEAT, TokenType.Keyword, TokenColor.Keyword, TokenTriggers.None);
            AddTokenDefinition((int)Tokens.KWRETURN, TokenType.Keyword, TokenColor.Keyword, TokenTriggers.None);
            AddTokenDefinition((int)Tokens.KWTHEN, TokenType.Keyword, TokenColor.Keyword, TokenTriggers.None);
            AddTokenDefinition((int)Tokens.KWTRUE, TokenType.Keyword, TokenColor.Keyword, TokenTriggers.None);
            AddTokenDefinition((int)Tokens.KWUNTIL, TokenType.Keyword, TokenColor.Keyword, TokenTriggers.None);
            AddTokenDefinition((int)Tokens.KWWHILE, TokenType.Keyword, TokenColor.Keyword, TokenTriggers.None);

            // Identifiers numbers and strings
            AddTokenDefinition((int)Tokens.IDENTIFIER, TokenType.Identifier, TokenColor.Identifier, TokenTriggers.None);
            AddTokenDefinition((int)Tokens.NUMBER, TokenType.Literal, TokenColor.Number, TokenTriggers.None);
            AddTokenDefinition((int)Tokens.STRING, TokenType.String, TokenColor.String, TokenTriggers.None);

            // Operators
            AddTokenDefinition((int)Tokens.PLUS, TokenType.Operator, TokenColor.Text, TokenTriggers.None);
            AddTokenDefinition((int)Tokens.MINUS, TokenType.Operator, TokenColor.Text, TokenTriggers.None);
            AddTokenDefinition((int)Tokens.ASTERISK, TokenType.Operator, TokenColor.Text, TokenTriggers.None);
            AddTokenDefinition((int)Tokens.SLASH, TokenType.Operator, TokenColor.Text, TokenTriggers.None);
            AddTokenDefinition((int)Tokens.PERCENT, TokenType.Operator, TokenColor.Text, TokenTriggers.None);
            AddTokenDefinition((int)Tokens.CARET, TokenType.Operator, TokenColor.Text, TokenTriggers.None);
            AddTokenDefinition((int)Tokens.POUND, TokenType.Operator, TokenColor.Text, TokenTriggers.None);
            AddTokenDefinition((int)Tokens.EQUAL, TokenType.Operator, TokenColor.Text, TokenTriggers.None);
            AddTokenDefinition((int)Tokens.EQ, TokenType.Operator, TokenColor.Text, TokenTriggers.None);
            AddTokenDefinition((int)Tokens.NEQ, TokenType.Operator, TokenColor.Text, TokenTriggers.None);
            AddTokenDefinition((int)Tokens.LTE, TokenType.Operator, TokenColor.Text, TokenTriggers.None);
            AddTokenDefinition((int)Tokens.GTE, TokenType.Operator, TokenColor.Text, TokenTriggers.None);
            AddTokenDefinition((int)Tokens.LT, TokenType.Operator, TokenColor.Text, TokenTriggers.None);
            AddTokenDefinition((int)Tokens.GT, TokenType.Operator, TokenColor.Text, TokenTriggers.None);
            AddTokenDefinition((int)Tokens.DOTDOT, TokenType.Operator, TokenColor.Text, TokenTriggers.None);

            // Delimiters
            AddTokenDefinition((int)Tokens.LPAREN, TokenType.Delimiter, TokenColor.Text, TokenTriggers.MatchBraces | TokenTriggers.ParameterStart);
            AddTokenDefinition((int)Tokens.RPAREN, TokenType.Delimiter, TokenColor.Text, TokenTriggers.MatchBraces | TokenTriggers.ParameterEnd);
            AddTokenDefinition((int)Tokens.LBRACE, TokenType.Delimiter, TokenColor.Text, TokenTriggers.MatchBraces);
            AddTokenDefinition((int)Tokens.RBRACE, TokenType.Delimiter, TokenColor.Text, TokenTriggers.MatchBraces);
            AddTokenDefinition((int)Tokens.LBRACKET, TokenType.Delimiter, TokenColor.Text, TokenTriggers.MemberSelect | TokenTriggers.MatchBraces);
            AddTokenDefinition((int)Tokens.RBRACKET, TokenType.Delimiter, TokenColor.Text, TokenTriggers.MatchBraces);

            AddTokenDefinition((int)Tokens.COLON, TokenType.Delimiter, TokenColor.Text, TokenTriggers.MemberSelect);
            AddTokenDefinition((int)Tokens.COMMA, TokenType.Delimiter, TokenColor.Text, TokenTriggers.ParameterNext);
            AddTokenDefinition((int)Tokens.DOT, TokenType.Delimiter, TokenColor.Text, TokenTriggers.MemberSelect);

            // Ellipsis
            AddTokenDefinition((int)Tokens.ELLIPSIS, TokenType.Text, TokenColor.Text, TokenTriggers.None);

            // Token definitions used only by the LineScanner / Colorizer and not the parser (token > maxParseToken)

            // TODO: Do we want a custom token color for our errors coming from Lex?

            AddTokenDefinition((int)Tokens.LEX_WHITESPACE, TokenType.WhiteSpace, TokenColor.Text, TokenTriggers.None);
            AddTokenDefinition((int)Tokens.LEX_COMMENT, TokenType.Comment, TokenColor.Comment, TokenTriggers.None);
            AddTokenDefinition((int)Tokens.LEX_LINECOMMENT, TokenType.LineComment, TokenColor.Comment, TokenTriggers.None);
            AddTokenDefinition((int)Tokens.LEX_ERROR, TokenType.Text, TokenColor.Text, TokenTriggers.None);
        }

        /// <summary>
        /// Gets the colorable items.
        /// </summary>
        public static IList<IVsColorableItem> ColorableItems
        {
            get { return colorableItems; }
        }

        /// <summary>
        /// Gets information about the comments.
        /// </summary>
        public static CommentInfo CommentInfo
        {
            get { return commentInfo; }
        }

        /// <summary>
        /// Creates the color.
        /// </summary>
        /// <param name="name">The name.</param>
        /// <param name="foreground">The foreground.</param>
        /// <param name="background">The background.</param>
        /// <returns></returns>
        public static TokenColor CreateTokenColor(string name, COLORINDEX foreground, COLORINDEX background)
        {
            return CreateTokenColor(name, foreground, background, false, false);
        }

        /// <summary>
        /// Creates the color.
        /// </summary>
        /// <param name="name">The name.</param>
        /// <param name="foreground">The foreground.</param>
        /// <param name="background">The background.</param>
        /// <param name="bold">if set to <c>true</c> [bold].</param>
        /// <param name="strikethrough">if set to <c>true</c> [strikethrough].</param>
        /// <returns></returns>
        public static TokenColor CreateTokenColor(string name, COLORINDEX foreground, COLORINDEX background, bool bold, bool strikethrough)
        {
            colorableItems.Add(new ColorableItem(name, foreground, background, bold, strikethrough));
            return (TokenColor)colorableItems.Count;
        }

        /// <summary>
        /// Colors the token.
        /// </summary>
        /// <param name="token">The token.</param>
        /// <param name="type">The type.</param>
        /// <param name="color">The color.</param>
        /// <param name="trigger">The trigger.</param>
        public static void AddTokenDefinition(int token, TokenType type, TokenColor color, TokenTriggers trigger)
        {
            definitions[token] = new TokenDefinition(type, color, trigger);
        }

        /// <summary>
        /// Gets the definition.
        /// </summary>
        /// <param name="token">The token.</param>
        /// <returns></returns>
        public static TokenDefinition GetTokenDefinition(int token)
        {
            TokenDefinition result;
            return definitions.TryGetValue(token, out result) ? result : defaultDefinition;
        }
    }
}